// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: DfgVertex sub-classes
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// This is a data-flow graph based representation of combinational logic,
// the main difference from a V3Graph is that DfgVertex owns the storage
// of it's input edges (operands/sources/arguments), and can access each
// input edge directly by indexing, making modifications more efficient
// than the linked list based structures used by V3Graph.
//
// A bulk of the DfgVertex sub-types are generated by astgen, and are
// analogous to the corresponding AstNode sub-types.
//
// See also the internals documentation docs/internals.rst
//
//*************************************************************************

#ifndef VERILATOR_V3DFGVERTICES_H_
#define VERILATOR_V3DFGVERTICES_H_

#ifndef VERILATOR_V3DFG_H_
#error "Use V3Dfg.h as the include"
#include "V3Dfg.h"  // This helps code analysis tools pick up symbols in V3Dfg.h
#define VL_NOT_FINAL  // This #define fixes broken code folding in the CLion IDE
#endif

// Include macros generated by 'astgen'. These include DFGGEN_MEMBERS_<Node>
// for each DfgVertex sub-type. The generated members include boilerplate
// methods related to cloning, visitor dispatch, and other functionality.
// For precise details please read the generated macros.
#include "V3Dfg__gen_macros.h"

//------------------------------------------------------------------------------
// Variable vertices - represent a variables

class DfgVertexVar VL_NOT_FINAL : public DfgVertex {
    // Represents a variable. It has 2 optional inputs, 'srcp' and 'defaultp'.

    AstVar* const m_varp;  // The AstVar associated with this vertex (not owned by this vertex)
    AstVarScope* const m_varScopep;  // The AstVarScope associated with this vertex (not owned)
    // Location of driver of this variable. Only used for converting back to Ast. Might be nullptr.
    FileLine* m_driverFileLine = nullptr;
    // If this DfgVertexVar is a synthesized temporary, this is the original Var/VarScope it stands
    // for. It might point to m_varp/m_varScopep itself to indicate it's a temporary without an
    // associated input Var/VarScope.
    AstNode* m_tmpForp = nullptr;

    DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, AstVarScope* vscp)
        : DfgVertex{dfg, type, varp->fileline(), *DfgDataType::fromAst(varp->dtypep())}
        , m_varp{varp}
        , m_varScopep{vscp} {
#ifdef VL_DEBUG
        if (v3Global.rootp()->topScopep()) {
            UASSERT_OBJ(vscp, varp, "Un-scoped DfgVertexVar created in scoped DfgGraph");
        } else {
            UASSERT_OBJ(!vscp, varp, "Scoped DfgVertexVar created in un-scoped DfgGraph");
        }
#endif
        // Increment reference count
        AstNode* const variablep = nodep();
        variablep->user1(variablep->user1() + 0x10);
        UASSERT_OBJ((variablep->user1() >> 4) > 0, variablep, "Reference count overflow");
        // Allocate sources
        newInput();
        newInput();
    }

protected:
    DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp)
        : DfgVertexVar{dfg, type, varp, nullptr} {}
    DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp)
        : DfgVertexVar{dfg, type, vscp->varp(), vscp} {}

public:
    ~DfgVertexVar() {
        // Decrement reference count
        AstNode* const variablep = nodep();
        variablep->user1(variablep->user1() - 0x10);
        UASSERT_OBJ((variablep->user1() >> 4) >= 0, variablep, "Reference count underflow");
    }
    ASTGEN_MEMBERS_DfgVertexVar;

    // The driver of the variable. Might be nullptr if driven exernally (input to DfgGraph).
    DfgVertex* srcp() const { return inputp(0); }
    void srcp(DfgVertex* vtxp) { inputp(0, vtxp); }
    // The default value of the variable. This defines the parts not driven by 'srcp', maybe null
    DfgVertex* defaultp() const { return inputp(1); }
    void defaultp(DfgVertex* vtxp) { inputp(1, vtxp); }

    std::string srcName(size_t idx) const override final { return idx ? "defaultp" : "srcp"; }

    // The Ast variable this vertex representess
    AstVar* varp() const { return m_varp; }
    AstVarScope* varScopep() const { return m_varScopep; }
    AstNode* nodep() const {
        return m_varScopep ? static_cast<AstNode*>(m_varScopep) : static_cast<AstNode*>(m_varp);
    }

    // If this is a temporary, the Ast variable it stands for,  or same as
    // 'nodep()' if it's a temporary with no associated original Ast variable.
    AstNode* tmpForp() const { return m_tmpForp; }
    void tmpForp(AstNode* nodep) { m_tmpForp = nodep; }

    // Location of driver of variable (only used if 'srcp' is not a splice)
    FileLine* driverFileLine() const { return m_driverFileLine; }
    void driverFileLine(FileLine* flp) { m_driverFileLine = flp; }

    // Variable referenced from other DFG in the same module/netlist
    bool hasDfgRefs() const { return nodep()->user1() >> 5; }  // I.e.: (nodep()->user1() >> 4) > 1

    // Variable referenced from Ast code in the same module/netlist
    static bool hasModRdRefs(const AstNode* nodep) { return nodep->user1() & 0x04; }
    static bool hasModWrRefs(const AstNode* nodep) { return nodep->user1() & 0x08; }
    static void setHasModRdRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x04); }
    static void setHasModWrRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x08); }
    bool hasModRdRefs() const { return hasModRdRefs(nodep()); }
    bool hasModWrRefs() const { return hasModWrRefs(nodep()); }
    bool hasModRefs() const { return hasModRdRefs() || hasModWrRefs(); }
    void setHasModRdRefs() const { setHasModRdRefs(nodep()); }
    void setHasModWrRefs() const { setHasModWrRefs(nodep()); }

    // Variable referenced outside the containing module/netlist.
    static bool hasExtRdRefs(const AstNode* nodep) { return nodep->user1() & 0x01; }
    static bool hasExtWrRefs(const AstNode* nodep) { return nodep->user1() & 0x02; }
    static void setHasExtRdRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x01); }
    static void setHasExtWrRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x02); }
    bool hasExtRdRefs() const { return hasExtRdRefs(nodep()); }
    bool hasExtWrRefs() const { return hasExtWrRefs(nodep()); }
    bool hasExtRefs() const { return hasExtRdRefs() || hasExtWrRefs(); }

    // The value of this vertex might differ from what is defined by its drivers
    // 'srcp' and 'defaultp'. That is, it might be assigned, possibly partially,
    // or abruptly outside the graph, hence it is not equivalent to its 'srcp'.
    static bool isVolatile(const AstNode* nodep) {
        return hasModWrRefs(nodep) || hasExtWrRefs(nodep);
    }
    bool isVolatile() const { return isVolatile(nodep()); }
};

class DfgVarArray final : public DfgVertexVar {
    friend class DfgVertex;
    friend class DfgVisitor;

public:
    DfgVarArray(DfgGraph& dfg, AstVar* varp)
        : DfgVertexVar{dfg, dfgType(), varp} {
        UASSERT_OBJ(isArray(), varp, "Non-array DfgVarArray");
    }
    DfgVarArray(DfgGraph& dfg, AstVarScope* vscp)
        : DfgVertexVar{dfg, dfgType(), vscp} {
        UASSERT_OBJ(isArray(), vscp, "Non-array DfgVarArray");
    }
    ASTGEN_MEMBERS_DfgVarArray;
};

class DfgVarPacked final : public DfgVertexVar {
    friend class DfgVertex;
    friend class DfgVisitor;

public:
    DfgVarPacked(DfgGraph& dfg, AstVar* varp)
        : DfgVertexVar{dfg, dfgType(), varp} {
        UASSERT_OBJ(isPacked(), varp, "Non-packed DfgVarPacked");
    }
    DfgVarPacked(DfgGraph& dfg, AstVarScope* vscp)
        : DfgVertexVar{dfg, dfgType(), vscp} {
        UASSERT_OBJ(isPacked(), vscp, "Non-packed DfgVarPacked");
    }
    ASTGEN_MEMBERS_DfgVarPacked;
};

//------------------------------------------------------------------------------
// Nullary vertices - 0 inputs

class DfgVertexNullary VL_NOT_FINAL : public DfgVertex {
protected:
    DfgVertexNullary(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertex{dfg, type, flp, dtype} {}

public:
    ASTGEN_MEMBERS_DfgVertexNullary;
    std::string srcName(size_t) const override final { V3ERROR_NA_RETURN(""); }
};

class DfgConst final : public DfgVertexNullary {
    friend class DfgVertex;
    friend class DfgVisitor;

    V3Number m_num;  // Constant value

public:
    DfgConst(DfgGraph& dfg, FileLine* flp, const V3Number& num)
        : DfgVertexNullary{dfg, dfgType(), flp, DfgDataType::packed(num.width())}
        , m_num{num} {}
    DfgConst(DfgGraph& dfg, FileLine* flp, size_t width, uint32_t value)
        : DfgVertexNullary{dfg, dfgType(), flp, DfgDataType::packed(width)}
        , m_num{flp, static_cast<int>(width), value} {}

    ASTGEN_MEMBERS_DfgConst;

    V3Number& num() { return m_num; }
    const V3Number& num() const { return m_num; }

    size_t toSizeT() const { return static_cast<size_t>(num().toUQuad()); }
    uint32_t toU32() const { return num().toUInt(); }

    bool isZero() const { return num().isEqZero(); }
    bool isOnes() const { return num().isEqAllOnes(width()); }

    // Does this DfgConst have the given value? Note this is not easy to answer if wider than 32.
    bool hasValue(uint32_t value) const {
        return !num().isFourState() && num().edataWord(0) == value && num().mostSetBitP1() <= 32;
    }
};

//------------------------------------------------------------------------------
// Unary vertices - 1 inputs

class DfgVertexUnary VL_NOT_FINAL : public DfgVertex {
protected:
    DfgVertexUnary(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertex{dfg, type, flp, dtype} {
        newInput();
    }

public:
    ASTGEN_MEMBERS_DfgVertexUnary;
    DfgVertex* srcp() const { return inputp(0); }
    void srcp(DfgVertex* vtxp) { inputp(0, vtxp); }
    std::string srcName(size_t) const override final { return ""; }
};

class DfgSel final : public DfgVertexUnary {
    // AstSel is binary, but 'lsbp' is very often constant. As AstSel is fairly
    // common, we special case as a DfgSel for the constant 'lsbp', and as
    // 'DfgMux` for the non-constant 'lsbp'.
    uint32_t m_lsb = 0;  // The LSB index

public:
    DfgSel(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexUnary{dfg, dfgType(), flp, dtype} {}
    ASTGEN_MEMBERS_DfgSel;

    DfgVertex* fromp() const { return srcp(); }
    void fromp(DfgVertex* vtxp) { srcp(vtxp); }
    uint32_t lsb() const { return m_lsb; }
    void lsb(uint32_t value) { m_lsb = value; }
};

class DfgUnitArray final : public DfgVertexUnary {
    // This is a type adapter for modeling arrays. It's a single element array,
    // with the value of the single element being the source operand.
public:
    DfgUnitArray(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexUnary{dfg, dfgType(), flp, dtype} {
        UASSERT_OBJ(isArray(), flp, "Non-array DfgUnitArray");
        UASSERT_OBJ(size() == 1, flp, "DfgUnitArray must have a single element");
    }
    ASTGEN_MEMBERS_DfgUnitArray;
};

//------------------------------------------------------------------------------
// Binary vertices - 2 inputs

class DfgVertexBinary VL_NOT_FINAL : public DfgVertex {
protected:
    DfgVertexBinary(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertex{dfg, type, flp, dtype} {
        newInput();
        newInput();
    }

public:
    ASTGEN_MEMBERS_DfgVertexBinary;
};

class DfgMux final : public DfgVertexBinary {
    // AstSel is binary, but 'lsbp' is very often constant. As AstSel is fairly
    // common, we special case as a DfgSel for the constant 'lsbp', and as
    // 'DfgMux` for the non-constant 'lsbp'.
public:
    DfgMux(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexBinary{dfg, dfgType(), flp, dtype} {}
    ASTGEN_MEMBERS_DfgMux;

    DfgVertex* fromp() const { return inputp(0); }
    void fromp(DfgVertex* vtxp) { inputp(0, vtxp); }
    DfgVertex* lsbp() const { return inputp(1); }
    void lsbp(DfgVertex* vtxp) { inputp(1, vtxp); }

    std::string srcName(size_t idx) const override { return idx ? "lsbp" : "fromp"; }
};

//------------------------------------------------------------------------------
// Ternary vertices - 3 inputs

class DfgVertexTernary VL_NOT_FINAL : public DfgVertex {
protected:
    DfgVertexTernary(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertex{dfg, type, flp, dtype} {
        newInput();
        newInput();
        newInput();
    }

public:
    ASTGEN_MEMBERS_DfgVertexTernary;
};

//------------------------------------------------------------------------------
// Variadic vertices - variable number of inputs

class DfgVertexVariadic VL_NOT_FINAL : public DfgVertex {
protected:
    DfgVertexVariadic(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertex{dfg, type, flp, dtype} {}

public:
    ASTGEN_MEMBERS_DfgVertexVariadic;
};

class DfgVertexSplice VL_NOT_FINAL : public DfgVertexVariadic {
    // Represents a partial update to a varibale

    struct DriverData final {
        uint32_t m_lo;  // Low index of range driven by this driver
        FileLine* m_flp;  // Location of this driver
        DriverData() = delete;
        DriverData(uint32_t lo, FileLine* flp)
            : m_lo{lo}
            , m_flp{flp} {}
    };
    std::vector<DriverData> m_driverData;  // Additional data associated with each driver

protected:
    DfgVertexSplice(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexVariadic{dfg, type, flp, dtype} {}

public:
    ASTGEN_MEMBERS_DfgVertexSplice;

    // Add driver
    void addDriver(DfgVertex* vtxp, uint32_t lo, FileLine* flp) {
        UASSERT_OBJ(!vtxp->is<DfgLogic>(), vtxp, "addDriver called with DfgLogic");
        m_driverData.emplace_back(lo, flp);
        newInput()->relinkSrcp(vtxp);
    }

    void resetDrivers() {
        resetInputs();
        m_driverData.clear();
    }

    std::string srcName(size_t idx) const override final {
        const uint32_t lo = m_driverData[idx].m_lo;
        const uint32_t hi = lo + inputp(idx)->size() - 1;
        return '[' + std::to_string(hi) + ':' + std::to_string(lo) + ']';
    }

    FileLine* driverFileLine(size_t idx) const { return m_driverData.at(idx).m_flp; }

    DfgVertex* driverAt(size_t idx) const {
        const size_t n = nInputs();
        for (size_t i = 0; i < n; ++i) {
            if (m_driverData[i].m_lo == idx) return inputp(i);
        }
        return nullptr;
    }

    // If drives the whole result explicitly (not through defaultp), this is
    // the actual driver this DfgVertexSplice can be replaced with.
    DfgVertex* wholep() {
        if (nInputs() != 1) return nullptr;
        if (m_driverData[0].m_lo != 0) return nullptr;
        DfgVertex* const vtxp = inputp(0);
        if (vtxp->size() != size()) return nullptr;
        if (const DfgUnitArray* const uap = vtxp->cast<DfgUnitArray>()) {
            if (DfgVertexSplice* const splicep = uap->srcp()->cast<DfgVertexSplice>()) {
                if (!splicep->wholep()) return nullptr;
            }
        }
        return vtxp;
    }

    bool foreachDriver(std::function<bool(DfgVertex&, uint32_t, FileLine*)> f) {
        const size_t n = nInputs();
        for (size_t i = 0; i < n; ++i) {
            if (f(*inputp(i), m_driverData[i].m_lo, m_driverData[i].m_flp)) return true;
        }
        return false;
    }
    bool foreachDriver(std::function<bool(const DfgVertex&, uint32_t, FileLine*)> f) const {
        const size_t n = nInputs();
        for (size_t i = 0; i < n; ++i) {
            if (f(*inputp(i), m_driverData[i].m_lo, m_driverData[i].m_flp)) return true;
        }
        return false;
    }
    bool foreachDriver(std::function<bool(DfgVertex&, uint32_t)> f) {
        const size_t n = nInputs();
        for (size_t i = 0; i < n; ++i) {
            if (f(*inputp(i), m_driverData[i].m_lo)) return true;
        }
        return false;
    }
    bool foreachDriver(std::function<bool(const DfgVertex&, uint32_t)> f) const {
        const size_t n = nInputs();
        for (size_t i = 0; i < n; ++i) {
            if (f(*inputp(i), m_driverData[i].m_lo)) return true;
        }
        return false;
    }
};

class DfgSpliceArray final : public DfgVertexSplice {
    friend class DfgVertex;
    friend class DfgVisitor;

public:
    DfgSpliceArray(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexSplice{dfg, dfgType(), flp, dtype} {
        UASSERT_OBJ(isArray(), flp, "Non-array DfgSpliceArray");
    }
    ASTGEN_MEMBERS_DfgSpliceArray;
};

class DfgSplicePacked final : public DfgVertexSplice {
    friend class DfgVertex;
    friend class DfgVisitor;

public:
    DfgSplicePacked(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
        : DfgVertexSplice{dfg, dfgType(), flp, dtype} {
        UASSERT_OBJ(isPacked(), flp, "Non-packed DfgSplicePacked");
    }
    ASTGEN_MEMBERS_DfgSplicePacked;
};

class DfgLogic final : public DfgVertexVariadic {
    // Generic vertex representing a whole combinational process
    AstAlways* const m_nodep;  // The Ast logic represented by this vertex
    AstScope* const m_scopep;  // The AstScope m_nodep is under, iff scoped
    const std::unique_ptr<CfgGraph> m_cfgp;
    std::vector<DfgVertex*> m_synth;  // Vertices this logic was synthesized into
    bool m_selectedForSynthesis = false;  // Logic selected for synthesis
    bool m_nonSynthesizable = false;  // Logic is not synthesizeable (by DfgSynthesis)
    bool m_reverted = false;  // Logic was synthesized (in part if non synthesizable) then reverted

public:
    DfgLogic(DfgGraph& dfg, AstAlways* nodep, AstScope* scopep, std::unique_ptr<CfgGraph> cfgp)
        : DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), DfgDataType::null()}
        , m_nodep{nodep}
        , m_scopep{scopep}
        , m_cfgp{std::move(cfgp)} {}

    ASTGEN_MEMBERS_DfgLogic;

    std::string srcName(size_t) const override final { return ""; }

    // Can only be driven by DfgVertexVar
    void addInput(DfgVertexVar* varp) { newInput()->relinkSrcp(varp); }

    // Accessors
    AstAlways* nodep() const { return m_nodep; }
    AstScope* scopep() const { return m_scopep; }
    CfgGraph& cfg() { return *m_cfgp; }
    const CfgGraph& cfg() const { return *m_cfgp; }
    std::vector<DfgVertex*>& synth() { return m_synth; }
    const std::vector<DfgVertex*>& synth() const { return m_synth; }
    bool selectedForSynthesis() const { return m_selectedForSynthesis; }
    void setSelectedForSynthesis() { m_selectedForSynthesis = true; }
    bool nonSynthesizable() const { return m_nonSynthesizable; }
    void setNonSynthesizable() { m_nonSynthesizable = true; }
    bool reverted() const { return m_reverted; }
    void setReverted() { m_reverted = true; }
};

class DfgUnresolved final : public DfgVertexVariadic {
    // Represents a collection of unresolved variable drivers before synthesis
public:
    DfgUnresolved(DfgGraph& dfg, const DfgVertexVar* vtxp)
        : DfgVertexVariadic{dfg, dfgType(), vtxp->fileline(), vtxp->dtype()} {}
    ASTGEN_MEMBERS_DfgUnresolved;

    std::string srcName(size_t) const override final { return ""; }

    // Can only be driven by DfgLogic or DfgVertexSplice
    void addDriver(DfgLogic* vtxp) { newInput()->relinkSrcp(vtxp); }
    void addDriver(DfgVertexSplice* vtxp) { newInput()->relinkSrcp(vtxp); }
};

//------------------------------------------------------------------------------
// The rest of the vertex types are generated by 'astgen' from AstNodeExpr
#include "V3Dfg__gen_auto_classes.h"

//------------------------------------------------------------------------------
// Inline method definitions

#endif
